#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMessageBox>
#include <QDebug>
#include <QTcpSocket>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
    , m_socket(new QTcpSocket(this))
    , m_transactionId(0)
{
    ui->setupUi(this);

    connect(m_socket, &QTcpSocket::stateChanged, this, &MainWindow::onSocketStateChanged);
    connect(m_socket, &QTcpSocket::errorOccurred, this, &MainWindow::onSocketError);
    connect(m_socket, &QTcpSocket::readyRead, this, &MainWindow::onSocketReadyRead);

    updateUiState();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_connectButton_clicked()
{
    QString host = ui->hostEdit->text();
    quint16 port = ui->portEdit->text().toUShort();

    m_socket->connectToHost(host, port);
}

void MainWindow::on_disconnectButton_clicked()
{
    m_socket->disconnectFromHost();
}

void MainWindow::on_readCoilsButton_clicked()
{
    quint16 startAddr = ui->addressSpin->value();
    quint16 count = ui->countSpin->value();

    QByteArray request = createModbusRequest(0x01, startAddr, count);
    sendRequest(request);
}

void MainWindow::on_readInputsButton_clicked()
{
    quint16 startAddr = ui->addressSpin->value();
    quint16 count = ui->countSpin->value();

    QByteArray request = createModbusRequest(0x02, startAddr, count);
    sendRequest(request);
}

void MainWindow::on_readHoldingRegsButton_clicked()
{
    quint16 startAddr = ui->addressSpin->value();
    quint16 count = ui->countSpin->value();

    QByteArray request = createModbusRequest(0x03, startAddr, count);
    sendRequest(request);
}

void MainWindow::on_readInputRegsButton_clicked()
{
    quint16 startAddr = ui->addressSpin->value();
    quint16 count = ui->countSpin->value();

    QByteArray request = createModbusRequest(0x04, startAddr, count);
    sendRequest(request);
}

void MainWindow::on_writeCoilButton_clicked()
{
    quint16 address = ui->addressSpin->value();
    bool value = ui->valueSpin->value() != 0;

    QByteArray request = createWriteCoilRequest(address, value);
    sendRequest(request);
}

void MainWindow::on_writeRegButton_clicked()
{
    quint16 address = ui->addressSpin->value();
    quint16 value = static_cast<quint16>(ui->valueSpin->value());

    QByteArray request = createWriteRegisterRequest(address, value);
    sendRequest(request);
}

void MainWindow::onSocketStateChanged(QAbstractSocket::SocketState state)
{
    qDebug() << "Socket state changed:" << state;
    updateUiState();
}

void MainWindow::onSocketError(QAbstractSocket::SocketError error)
{
    QMessageBox::warning(this, "Connection Error", m_socket->errorString());
}

void MainWindow::onSocketReadyRead()
{
    QByteArray response = m_socket->readAll();
    parseModbusResponse(response);
}

QByteArray MainWindow::createModbusRequest(quint8 functionCode, quint16 startAddress, quint16 count)
{
    QByteArray request;
    QDataStream stream(&request, QIODevice::WriteOnly);
    stream.setByteOrder(QDataStream::BigEndian);

    // MBAP Header
    stream << m_transactionId;      // Transaction Identifier
    stream << quint16(0);           // Protocol Identifier (0 for Modbus)
    stream << quint16(6);           // Length (unit identifier + following bytes)
    stream << quint8(1);            // Unit Identifier

    // PDU
    stream << functionCode;
    stream << startAddress;
    stream << count;

    return request;
}

QByteArray MainWindow::createWriteCoilRequest(quint16 address, bool value)
{
    QByteArray request;
    QDataStream stream(&request, QIODevice::WriteOnly);
    stream.setByteOrder(QDataStream::BigEndian);

    // MBAP Header
    stream << m_transactionId;      // Transaction Identifier
    stream << quint16(0);           // Protocol Identifier
    stream << quint16(6);           // Length
    stream << quint8(1);            // Unit Identifier

    // PDU
    stream << quint8(0x05);         // Function Code
    stream << address;
    stream << (value ? quint16(0xFF00) : quint16(0x0000));

    return request;
}

QByteArray MainWindow::createWriteRegisterRequest(quint16 address, quint16 value)
{
    QByteArray request;
    QDataStream stream(&request, QIODevice::WriteOnly);
    stream.setByteOrder(QDataStream::BigEndian);

    // MBAP Header
    stream << m_transactionId;      // Transaction Identifier
    stream << quint16(0);           // Protocol Identifier
    stream << quint16(6);           // Length
    stream << quint8(1);            // Unit Identifier

    // PDU
    stream << quint8(0x06);         // Function Code
    stream << address;
    stream << value;

    return request;
}

void MainWindow::sendRequest(const QByteArray &request)
{
    if (m_socket->state() != QAbstractSocket::ConnectedState) {
        QMessageBox::warning(this, "Error", "Not connected to server");
        return;
    }

    m_socket->write(request);
    m_transactionId++;
}

void MainWindow::parseModbusResponse(const QByteArray &response)
{
    if (response.size() < 9) {
        qWarning() << "Response too short";
        return;
    }

    QDataStream stream(response);
    stream.setByteOrder(QDataStream::BigEndian);

    quint16 transactionId;
    quint16 protocolId;
    quint16 length;
    quint8 unitId;
    quint8 functionCode;

    stream >> transactionId;
    stream >> protocolId;
    stream >> length;
    stream >> unitId;
    stream >> functionCode;

    // Check for error
    if (functionCode & 0x80) {
        quint8 errorCode;
        stream >> errorCode;
        QString errorMsg;

        switch (errorCode) {
        case 0x01: errorMsg = "Illegal Function"; break;
        case 0x02: errorMsg = "Illegal Data Address"; break;
        case 0x03: errorMsg = "Illegal Data Value"; break;
        case 0x04: errorMsg = "Server Device Failure"; break;
        default: errorMsg = "Unknown Error"; break;
        }

        ui->statusLabel->setText(QString("Error: %1").arg(errorMsg));
        return;
    }

    switch (functionCode) {
    case 0x01: // Read Coils
    case 0x02: // Read Inputs
    {
        quint8 byteCount;
        stream >> byteCount;

        QVector<bool> values;
        for (int i = 0; i < byteCount; i++) {
            quint8 byte;
            stream >> byte;
            for (int bit = 0; bit < 8; bit++) {
                values.append(byte & (1 << bit));
            }
        }

        QString result;
        for (int i = 0; i < values.size() && i < ui->countSpin->value(); i++) {
            result += QString("Address %1: %2\n").arg(ui->addressSpin->value() + i).arg(values[i] ? "ON" : "OFF");
        }
        ui->resultText->setPlainText(result);
        break;
    }

    case 0x03: // Read Holding Registers
    case 0x04: // Read Input Registers
    {
        quint8 byteCount;
        stream >> byteCount;

        QVector<quint16> values;
        for (int i = 0; i < byteCount / 2; i++) {
            quint16 value;
            stream >> value;
            values.append(value);
        }

        QString result;
        for (int i = 0; i < values.size() && i < ui->countSpin->value(); i++) {
            result += QString("Address %1: %2\n").arg(ui->addressSpin->value() + i).arg(values[i]);
        }
        ui->resultText->setPlainText(result);
        break;
    }

    case 0x05: // Write Coil
    case 0x06: // Write Register
    {
        quint16 address;
        quint16 value;
        stream >> address;
        stream >> value;

        ui->statusLabel->setText("Write operation successful");
        break;
    }
    }
}

void MainWindow::updateUiState()
{
    bool connected = (m_socket->state() == QAbstractSocket::ConnectedState);
    ui->connectButton->setEnabled(!connected);
    ui->disconnectButton->setEnabled(connected);
    ui->readCoilsButton->setEnabled(connected);
    ui->readInputsButton->setEnabled(connected);
    ui->readHoldingRegsButton->setEnabled(connected);
    ui->readInputRegsButton->setEnabled(connected);
    ui->writeCoilButton->setEnabled(connected);
    ui->writeRegButton->setEnabled(connected);

    ui->statusLabel->setText(connected ? "Connected" : "Disconnected");
}
